// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_
#define CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_

#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/raw_ref.h"
#include "content/browser/private_aggregation/private_aggregation_budget_key.h"
#include "content/common/content_export.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/receiver_set.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/shared_storage/shared_storage_utils.h"
#include "third_party/blink/public/mojom/private_aggregation/private_aggregation_host.mojom.h"

namespace base {
class Time;
class Uuid;
}  // namespace base
namespace url {
class Origin;
}  // namespace url

namespace content {

class AggregatableReportRequest;
class BrowserContext;

// UI thread class responsible for implementing the mojo interface used by
// worklets and renderers to request reports be sent and maintaining the
// receiver set for this interface. It is responsible for validating the
// messages received and then passing them on for budget approval
class CONTENT_EXPORT PrivateAggregationHost
    : public blink::mojom::PrivateAggregationHost {
 public:
  // These values are persisted to logs. Entries should not be renumbered and
  // numeric values should never be reused.
  enum class SendHistogramReportResult {
    kSuccess = 0,
    kApiDisabledInSettings = 1,
    kSuccessButTruncatedDueToTooManyContributions = 2,
    kDebugKeyPresentWithoutDebugMode = 3,
    kReportRequestCreationFailed = 4,
    kNegativeValue = 5,
    kPipeWithContextIdReused = 6,
    kMaxValue = kPipeWithContextIdReused,
  };

  // Version string for the reports generated by this API.
  static constexpr char kApiReportVersion[] = "0.1";

  // The maximum number of contributions that can go in an `AggregatableReport`.
  // Aligns with `blink::kMaxAttributionAggregationKeysPerSourceOrTrigger`.
  static constexpr int kMaxNumberOfContributions = 50;

  // The maximum allowed context_id string length.
  static constexpr int kMaxContextIdLength = 64;
  static_assert(kMaxContextIdLength ==
                    blink::kPrivateAggregationApiContextIdMaxLength,
                "Maximum length of context_id should be aligned between Shared "
                "Storage and Private Aggregation.");

  // `on_report_request_received` and `browser_context` must be non-null.
  PrivateAggregationHost(
      base::RepeatingCallback<void(AggregatableReportRequest,
                                   PrivateAggregationBudgetKey)>
          on_report_request_received,
      BrowserContext* browser_context);
  PrivateAggregationHost(const PrivateAggregationHost&) = delete;
  PrivateAggregationHost& operator=(const PrivateAggregationHost&) = delete;
  ~PrivateAggregationHost() override;

  // Binds a new pending receiver for a worklet, allowing messages to be sent
  // and processed. However, the receiver is not bound if the `worklet_origin`
  // is not potentially trustworthy or if `context_id` is too long. The return
  // value indicates whether the receiver was accepted. Virtual for testing.
  [[nodiscard]] virtual bool BindNewReceiver(
      url::Origin worklet_origin,
      url::Origin top_frame_origin,
      PrivateAggregationBudgetKey::Api api_for_budgeting,
      absl::optional<std::string> context_id,
      mojo::PendingReceiver<blink::mojom::PrivateAggregationHost>
          pending_receiver);

  // blink::mojom::PrivateAggregationHost:
  void SendHistogramReport(
      std::vector<blink::mojom::AggregatableReportHistogramContributionPtr>
          contribution_ptrs,
      blink::mojom::AggregationServiceMode aggregation_mode,
      blink::mojom::DebugModeDetailsPtr debug_mode_details) override;
  void SetDebugModeDetailsOnNullReport(
      blink::mojom::DebugModeDetailsPtr debug_mode_details) override;

  void FlushReceiverSetForTesting() { receiver_set_.FlushForTesting(); }

 private:
  friend class PrivateAggregationReportGoldenLatestVersionTest;

  struct ReceiverContext;

  static absl::optional<AggregatableReportRequest> GenerateReportRequest(
      std::vector<blink::mojom::AggregatableReportHistogramContribution>
          contributions,
      blink::mojom::AggregationServiceMode aggregation_mode,
      blink::mojom::DebugModeDetailsPtr debug_mode_details,
      base::Time scheduled_report_time,
      base::Uuid report_id,
      const url::Origin& reporting_origin,
      PrivateAggregationBudgetKey::Api api_for_budgeting,
      absl::optional<std::string> context_id);

  void OnReceiverDisconnected();

  // Set iff the private aggregation developer mode is set.
  bool should_not_delay_reports_;

  base::RepeatingCallback<void(AggregatableReportRequest,
                               PrivateAggregationBudgetKey)>
      on_report_request_received_;

  mojo::ReceiverSet<blink::mojom::PrivateAggregationHost, ReceiverContext>
      receiver_set_;

  // `this` is indirectly owned by the StoragePartitionImpl, which itself is
  // owned by `browser_context_`.
  raw_ref<BrowserContext> browser_context_;
};

}  // namespace content

#endif  // CONTENT_BROWSER_PRIVATE_AGGREGATION_PRIVATE_AGGREGATION_HOST_H_
